﻿using CgdataBase.ViewModels;
using CgdataBase.Views;
using DiffEngine;
using DynamicData.Binding;
using FindLatestFile.Models;
using Mapster;

namespace FindLatestFile.ViewModels;

public class MainWindowViewModel : ViewModelBase
{
    private readonly IAppSettings _settings;
    private readonly IEventAggregator _aggregator;
    private readonly IContainerProvider _container;
    private CancellationTokenSource? _cts;

    public int FontSize => _settings.FontSize;
    public string FontName => _settings.FontName;

    public int CurrentIndex { get; set; }
    public bool DeleteToRecycleBin { get; set; } = true;
    public bool OrderByDescending { get; set; }
    public bool ShowAllFiles { get; set; }

    public FileInfoEx[]? ShowItems { get; set; }
    public int Count { get; set; }

    public ObservableCollection<FileInfoEx> Items { get; set; }
    public FileInfoEx? SelectedItem { get; set; }
    public IEnumerable? SelectedItems { get; set; }

    public OrderType[] Orders => Enum.GetValues<OrderType>();
    public OrderType Order { get; set; }

    public DelegateCommand<MouseButtonEventArgs> DoubleClickCommand { get; }
    public DelegateCommand OpenFolderCommand { get; }
    public DelegateCommand DeleteCommand { get; }
    public DelegateCommand MoveToRecycleBinCommand { get; }
    public DelegateCommand ClosingCommand { get; }
    public DelegateCommand<DragEventArgs> DropCommand { get; }
    public DelegateCommand CopyNameCommand { get; }
    public DelegateCommand CopyFullNameCommand { get; }
    public DelegateCommand CancelCommand { get; }
    public DelegateCommand CompareFilesCommand { get; }
    public DelegateCommand RefreshCommand { get; }
    public DelegateCommand MarkCommand { get; }
    public DelegateCommand ProcessedCommand { get; }
    public DelegateCommand IgnoreDuplicateFilesCommand { get; }
    public DelegateCommand IgnoreFileCommand { get; }
    public DelegateCommand LoadedCommand { get; }
    public DelegateCommand ClearAllCommand { get; }
    public DelegateCommand AboutCommand { get; }
    public DelegateCommand SettingsCommand { get; }
    public DelegateCommand QuitCommand { get; }

    public MainWindowViewModel(IAppSettings settings, IContainerProvider container,
        IEventAggregator aggregator)
    {
        _settings = settings;
        _aggregator = aggregator;
        _container = container;

        Title = App.ApplicationName;

        _settings.Adapt(this);
        Items = _settings.FileItems.ToObservableCollection();
        Items.CollectionChanged += (s, e) => OnRefresh();

        DoubleClickCommand = new DelegateCommand<MouseButtonEventArgs>(OnDoubleClick);
        OpenFolderCommand = new DelegateCommand(OnOpenFolder, CanEdit).ObservesProperty(() => SelectedItem);
        DeleteCommand = new DelegateCommand(OnDelete, CanEdit).ObservesProperty(() => SelectedItem);
        CopyNameCommand = new DelegateCommand(OnCopyName, CanEdit).ObservesProperty(() => SelectedItem);
        CopyFullNameCommand = new DelegateCommand(OnCopyFullName, CanEdit).ObservesProperty(() => SelectedItem);
        MoveToRecycleBinCommand = new DelegateCommand(OnMoveToRecycleBin, CanEdit).ObservesProperty(() => SelectedItem);
        DropCommand = new DelegateCommand<DragEventArgs>(OnDrop);
        CancelCommand = new DelegateCommand(OnCancel);
        CompareFilesCommand = new DelegateCommand(OnCompareFiles);
        RefreshCommand = new DelegateCommand(OnRefresh);
        AboutCommand = new DelegateCommand(OnAbout);
        SettingsCommand = new DelegateCommand(OnSettings);
        QuitCommand = new DelegateCommand(OnQuit);

        MarkCommand = new DelegateCommand(OnMark, CanEdit).ObservesProperty(() => SelectedItem);
        ProcessedCommand = new DelegateCommand(OnProcessed, CanEdit).ObservesProperty(() => SelectedItem);
        IgnoreDuplicateFilesCommand = new DelegateCommand(OnIgnoreDuplicateFiles);
        IgnoreFileCommand = new DelegateCommand(OnIgnoreFile, CanEdit).ObservesProperty(() => SelectedItem);
        ClearAllCommand = new DelegateCommand(OnClearAll);

        LoadedCommand = new DelegateCommand(OnLoaded);
        ClosingCommand = new DelegateCommand(OnClosing);

        this.WhenAnyPropertyChanged(nameof(Items),
            nameof(ShowAllFiles),
            nameof(Order),
            nameof(OrderByDescending))
            .Subscribe(s => OnRefresh());
    }

    private void OnQuit()
    {
        App.Current.Shutdown();
    }

    private void OnAbout()
    {
        var vm = _container.Resolve<WinAboutViewModel>();
        var win = new WinAbout() { Owner = App.Current.MainWindow, DataContext = vm };
        vm.LoadInfo(_settings, App.ApplicationName);
        win.ShowDialog();
    }

    private void OnSettings()
    {
        var vm = _container.Resolve<WinSettingsBaseViewModel>();
        var win = new WinSettingsBase() { Owner = App.Current.MainWindow, DataContext = vm };
        vm.LoadInfo(_settings, SettingsMode.默认);
        if (win.ShowDialog() == true)
        {
            RaisePropertyChanged(nameof(FontName));
            RaisePropertyChanged(nameof(FontSize));
        }
    }

    private void OnClearAll()
    {
        try
        {
            Items.Clear();
            OnRefresh();
        }
        catch (Exception ex)
        {
            _logger.Error(ex);
            ShowError(ex.Message);
        }
    }

    private void OnLoaded()
    {
        OnRefresh();
    }

    private void OnIgnoreFile()
    {
        try
        {
            if (SelectedItems == null)
                return;

            var items = SelectedItems.Cast<FileInfoEx>().ToArray();
            foreach (var item in items)
            {
                item.IsIgnored = true;
            }
            OnRefresh();
        }
        catch (Exception ex)
        {
            _logger.Error(ex);
            ShowError(ex.Message);
        }
    }

    public FileInfoEx[]? GetFileInfos()
    {
        if (Items.IsNullOrEmptyList())
        {
            return null;
        }

        IEnumerable<FileInfoEx> items = Items;
        if (ShowAllFiles == false)
        {
            items = items.Where(s => s.IsIgnored == false);
        }

        if (OrderByDescending)
        {
            switch (Order)
            {
                case OrderType.修改时间:
                    items = items.OrderBy(s => s.State).ThenByDescending(s => s.LastWriteTime);
                    break;

                case OrderType.哈希值:
                    items = items.OrderBy(s => s.State).ThenByDescending(s => s.HashCode);
                    break;

                case OrderType.文件大小:
                    items = items.OrderBy(s => s.State).ThenByDescending(s => s.Length);
                    break;

                case OrderType.完整路径:
                    items = items.OrderBy(s => s.State).ThenByDescending(s => s.FullName);
                    break;

                case OrderType.扩展名:
                    items = items.OrderBy(s => s.State).ThenByDescending(s => s.Extension);
                    break;

                default:
                    break;
            }
        }
        else
        {
            switch (Order)
            {
                case OrderType.修改时间:
                    items = items.OrderBy(s => s.State).ThenBy(s => s.LastWriteTime);
                    break;

                case OrderType.哈希值:
                    items = items.OrderBy(s => s.State).ThenBy(s => s.HashCode);
                    break;

                case OrderType.文件大小:
                    items = items.OrderBy(s => s.State).ThenBy(s => s.Length);
                    break;

                case OrderType.完整路径:
                    items = items.OrderBy(s => s.State).ThenBy(s => s.FullName);
                    break;

                case OrderType.扩展名:
                    items = items.OrderBy(s => s.State).ThenBy(s => s.Extension);
                    break;

                default:
                    break;
            }
        }

        return items.ToArray();
    }

    private void OnIgnoreDuplicateFiles()
    {
        try
        {
            if (ShowItems == null || ShowItems.Count() == 0)
                return;

            var items = ShowItems.ToArray();
            var groups = items.GroupBy(s => s.HashCode).Where(s => s.Count() > 1).ToArray();
            foreach (var group in groups)
            {
                var its = group.Skip(1).ToArray();
                foreach (var item in its)
                {
                    item.IsIgnored = true;
                }
            }
            OnRefresh();
        }
        catch (Exception ex)
        {
            _logger.Error(ex);
            ShowError(ex.Message);
        }
    }

    private void OnRefresh()
    {
        ShowItems = GetFileInfos();
        Count = ShowItems?.Length ?? 0;
    }

    private void OnProcessed()
    {
        UpdateState(FileState.已处理);
    }

    private void UpdateState(FileState state)
    {
        if (SelectedItems == null)
        {
            return;
        }

        var items = SelectedItems.Cast<FileInfoEx>().ToArray();
        items.ForEach(s => s.State = state);
    }

    private void OnMark()
    {
        UpdateState(FileState.标志);
    }

    private async void OnCompareFiles()
    {
        try
        {
            if (SelectedItems != null)
            {
                var items = SelectedItems.Cast<FileInfoEx>().ToArray();
                if (items.Length == 2)
                {
                    items = items.OrderBy(s => s.State == FileState.标志).ToArray();
                    await DiffRunner.LaunchAsync(items[0].FullName, items[1].FullName);
                    UpdateFileState(items);
                    SelectedItems = null;
                    return;
                }
            }

            if (ShowItems == null || ShowItems.Length < 2)
                return;

            var list = new List<FileInfoEx>();
            var info = ShowItems.FirstOrDefault(s => s.State == FileState.标志);
            if (info != null)
            {
                list.Add(info);
            }

            list.AddRange(ShowItems.Where(s => s.State == FileState.待处理).Take(2 - list.Count));
            await DiffRunner.LaunchAsync(list[0].FullName, list[1].FullName);
            UpdateFileState(list);
            SelectedItems = null;
        }
        catch (Exception ex)
        {
            _logger.Error(ex);
            ShowError(ex.Message);
        }
    }

    private void UpdateFileState(IEnumerable<FileInfoEx> items)
    {
        foreach (var file in items)
        {
            if (file.State == FileState.待处理)
            {
                file.State = FileState.已处理;
            }
        }
    }

    private void OnCancel()
    {
        try
        {
            _cts?.Cancel();
        }
        catch (Exception ex)
        {
            _logger.Error(ex);
            ShowError(ex.Message);
        }
    }

    private void OnCopyName()
    {
        if (SelectedItems == null)
        {
            return;
        }

        var items = SelectedItems.Cast<FileInfoEx>().ToArray();
        ClipboardEx.SetText(items.Select(s => s.FullName).StringJoin("\r\n"));
        ShowMessage("已复制");
    }

    private void OnCopyFullName()
    {
        if (SelectedItems == null)
        {
            return;
        }

        var items = SelectedItems.Cast<FileInfoEx>().ToArray();
        ClipboardEx.SetText(items.Select(s => s.FullName).StringJoin("\r\n"));
        ShowMessage("已复制");
    }

    private void OnDrop(DragEventArgs e)
    {
        try
        {
            var fileNames = e.Data.GetData(DataFormats.FileDrop, false) as string[];
            if (fileNames == null || fileNames.Length == 0)
            {
                return;
            }

            foreach (var item in fileNames)
            {
                if (File.Exists(item))
                {
                    if (Items.Any(s => s.FullName == item) == false)
                        Items.Add(FileInfoEx.GetFileInfo(item));
                }
            }
        }
        catch (Exception ex)
        {
            _logger.Error(ex);
            ShowError(ex.Message);
        }
    }

    private async void OnMoveToRecycleBin()
    {
        try
        {
            if (SelectedItems == null)
                return;

            _cts = new CancellationTokenSource();
            var token = _cts.Token;
            var items = SelectedItems.Cast<FileInfoEx>().ToArray();
            using (var scope = new BusyScope(ApplicationBase.GetBusyScope()))
            {
                foreach (var item in items)
                {
                    if (token.IsCancellationRequested)
                    {
                        return;
                    }

                    await Task.Run(() => FileSystemHelper.DeleteFileSystemEntry(item.FullName), token);
                    Items.Remove(item);
                }
            }
        }
        catch (Exception ex)
        {
            _logger.Error(ex);
            ShowError(ex.Message);
        }
    }

    private void OnClosing()
    {
        this.Adapt(_settings);
        _settings.FileItems = Items.ToArray();
        _settings.SaveConfig();
    }

    private async void OnDelete()
    {
        try
        {
            if (SelectedItems == null)
                return;

            if (DeleteToRecycleBin)
            {
                OnMoveToRecycleBin();
            }
            else
            {
                _cts = new CancellationTokenSource();
                var token = _cts.Token;

                var items = SelectedItems.Cast<FileInfoEx>().ToArray();
                using (var scope = new BusyScope(ApplicationBase.GetBusyScope()))
                {
                    foreach (var item in items)
                    {
                        if (token.IsCancellationRequested)
                        {
                            return;
                        }

                        await Task.Run(() => FileSystemHelper.DeleteFileSystemEntry(item.FullName));
                        Items.Remove(item);
                    }
                }
            }
            OnRefresh();
        }
        catch (Exception ex)
        {
            _logger.Error(ex);
            ShowError(ex.Message);
        }
    }

    private void OnOpenFolder()
    {
        try
        {
            if (SelectedItem == null)
            {
                return;
            }

            Process.Start("explorer", $"/SELECT,{SelectedItem.FullName}");
        }
        catch (Exception ex)
        {
            _logger.Error(ex);
            ShowError(ex.Message);
        }
    }

    private bool CanEdit()
    {
        return SelectedItem != null;
    }

    private void OnDoubleClick(MouseButtonEventArgs e)
    {
        var list = e.Source as DataGrid;
        var item = UIElementHelper.GetElementFromPoint(list, e.GetPosition(list));
        if (item != null && SelectedItem != null)
        {
            var file = SelectedItem;
            Process.Start(new ProcessStartInfo(file.FullName) { UseShellExecute = true });
        }
    }
}